package com.yanyeori.framework.security.util;

import com.yanyeori.framework.core.constant.ChannelEnum;
import com.yanyeori.framework.core.context.UserContext;
import com.yanyeori.framework.core.model.WebResponse;
import com.yanyeori.framework.core.util.BeanUtil;
import com.yanyeori.framework.core.util.StringUtil;
import com.yanyeori.framework.security.bo.UserDetail;
import com.yanyeori.framework.security.config.ConfigFactory;
import com.yanyeori.framework.security.config.SecurityAutoConfig;
import com.yanyeori.framework.security.constant.SecurityConst;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * SSO工具类
 *
 * @author chenkuan 2020/11/22
 */
public class SecurityUtil {

    private static String[] anonApis = new String[]{};

    /**
     * 从request中获取auth token
     *
     * @return token
     */
    public static WebResponse<String> getToken(HttpServletRequest request) {
        String token = request.getHeader(SecurityConst.AUTHORIZATION);
        String path = getServletPath(request);
        if (StringUtil.isEmpty(token)) {
            return WebResponse.fail("Request url [{}], header not set token [{}]", path, SecurityConst.AUTHORIZATION);
        }
        return WebResponse.ok(token);
    }

    /**
     * 从request中获取Timestamp
     */
    public static Long getTimestamp(HttpServletRequest request) {
        String timestamp = request.getHeader(SecurityConst.AUTH_TIMESTAMP);
        return StringUtil.isEmpty(timestamp) ? null : Long.parseLong(timestamp);
    }

    /**
     * 从request中获取token签名
     */
    public static String getSign(HttpServletRequest request) {
        return request.getHeader(SecurityConst.AUTH_SIGN);
    }

    /**
     * 从request中获取ServletPath
     */
    public static String getServletPath(HttpServletRequest request) {
        return request.getServletPath();
    }

    /**
     * 从request中获取channel
     */
    public static ChannelEnum getChannel(HttpServletRequest request) {
        String channel = request.getHeader(SecurityConst.HEADER_CHANNEL);
        if (StringUtil.isEmpty(channel)) return ChannelEnum.WEB;
        return ChannelEnum.get(channel);
    }

    /**
     * 模糊匹配url
     *
     * @param patternUrl 匹配表达式
     * @param requestUrl 待匹配url
     */
    public static boolean matchPath(String patternUrl, String requestUrl) {
        if (StringUtil.isEmpty(patternUrl) || StringUtil.isEmpty(requestUrl)) {
            return false;
        }
        PathMatcher matcher = new AntPathMatcher();
        return matcher.match(patternUrl, requestUrl);
    }

    /**
     * 模糊匹配IP，支持IPv4、IPv6
     *
     * @param patternIp 匹配表达式
     * @param requestIp 待匹配ip
     */
    public static boolean matchIp(String patternIp, String requestIp) {
        final String SIGN_PATTERN = "*";
        final String SIGN_IPV4 = ".";
        final String SIGN_IPV6 = ":";
        if (StringUtil.isEmpty(patternIp) || StringUtil.isEmpty(requestIp)) {
            return false;
        }
        if (!patternIp.contains(SIGN_PATTERN)) {
            return patternIp.equals(requestIp);
        }
        if (patternIp.equals(SIGN_PATTERN)) {
            return true;
        }
        final String[] patternIps;
        final String[] requestIps;
        if (requestIp.contains(SIGN_IPV4)) {
            //IPv4
            patternIps = StringUtil.split(patternIp, SIGN_IPV4);
            requestIps = StringUtil.split(requestIp, SIGN_IPV4);
        } else if (requestIp.contains(SIGN_IPV6)) {
            //IPv6
            patternIps = StringUtil.split(patternIp, SIGN_IPV6);
            requestIps = StringUtil.split(requestIp, SIGN_IPV6);
        } else {
            throw new IllegalArgumentException("Unknown IP address format");
        }
        if (patternIps.length != requestIps.length) {
            return false;
        }
        for (int i = 0; i < patternIps.length; i++) {
            if (!patternIps[i].equals(SIGN_PATTERN) && !patternIps[i].equals(requestIps[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断访问地址是否匿名接口
     */
    public static boolean isAnonApi(String url) {
        return isAnonApi(url, null);
    }

    /**
     * 判断访问地址是否匿名接口
     */
    public static boolean isAnonApi(String url, List<String> annApis) {
        if (anonApis.length == 0 || ConfigFactory.getConfig().isDevMode()) {
            anonApis = ArrayUtils.addAll(SecurityConst.HTTP_ANON_API, SecurityAutoConfig.getAnonymousUrlSet().toArray(new String[0]));
            if (ArrayUtils.isNotEmpty(ConfigFactory.getConfig().getAnonApi())) {
                anonApis = ArrayUtils.addAll(anonApis, ConfigFactory.getConfig().getAnonApi());
            }
            if (CollectionUtils.isNotEmpty(annApis)) {
                anonApis = ArrayUtils.addAll(anonApis, annApis.toArray(new String[annApis.size()]));
            }
        }
        for (String api : anonApis) {
            if (matchPath(api, url)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 保存UserContext上下文信息，并返回UserContext
     */
    public static UserContext saveUserContext(UserDetail userDetail) {
        UserContext userContext = BeanUtil.copyProperties(userDetail, UserContext.class);
        UserContext.set(userContext);
        return userContext;
    }
}
